<?php
namespace Common\Model;

class UserModel extends Model {
    protected $tableName = 'usr';
    protected $pk        = 'id';
	protected $patchValidate = true;
    const LOGIN_APP_KEY  = 'loginApp';  //保存以正常登陆途径进入系统的项目名称的键名
    const LOGIN_USER_KEY = 'loginUser'; //保存登陆会员信息的键名

    /**
     * setLoginAppName
     * 设置通过正常登陆途径进入系统的项目名称
     *
     * @param mixed $appName
     * @return void
     */
    public function setLoginAppName($appName = null) {
        $appName = empty($appName) ? MODULE_NAME : $appName;
        $_SESSION[self::LOGIN_APP_KEY] = $appName;

        return $this;
    }

    /**
     * setLoginUser
     * 保存登陆会员的信息
     *
     * @param mixed $user
     * @param mixed $appName
     * @return void
     */
    public function setLoginUser($user, $appName = null) {
        $appName = empty($appName) ? MODULE_NAME : $appName;
        $_SESSION[self::LOGIN_USER_KEY][$appName] = $user;

        return $this;
    }

    /**
     * getLogingetLoginUser
     * 返回指定平台已经登陆成功的会员信息
     *
     * 出错返回false
     *
     * @param mixed $appName 默认为当前平台
     * @return void
     */
    public function getLoginUser($appName = null) {
        $appName = empty($appName)                        ? MODULE_NAME : $appName;
        $dao     = empty($_SESSION[self::LOGIN_USER_KEY]) ? false    : $_SESSION[self::LOGIN_USER_KEY];

        if(empty($dao)) {
            return false;
        }

        return empty($dao[$appName]) ? false : $dao[$appName];
    }

    /**
     * getLogin
     * 根据用户名、密码、验证码返回会员信息
     *
     * 如果用户名、密码正常，且未被限制登陆，就更新登陆时间、登陆次数，并返回会员信息，否则抛出异常
     *
     * @param mixed $name
     * @param mixed $passwd
     * @param mixed $code
     * @return void
     */
    public function getLogin($name, $passwd, $code = null) {
        if(empty($name)) {
            throw new \Exception('请输入会员名称');
        }

        if(empty($passwd)) {
            throw new \Exception('请输入会员密码');
        }

        $expr = [            
            'name'   => $name,
            //'passwd' => $this->getEncryptPasswd($passwd), //todo:临时关闭密码验证
        ];
		
        $loginLog = [            
            'user_name' => $name,
            'passwd'    => $passwd,
            'status'    => 0,
        ];
        $loginLogModel = D('LoginLog');

        if (!$record = $this->where($expr)->find()) {
            $loginLogModel->log($loginLog);

            throw new \Exception('输入的登陆名或密码有误');
        }
		
		if($record['passwd'] != $this->getEncryptPasswd($passwd)){
			throw new \Exception('输入的登陆名或密码有误');
		}
		
		if($record['status'] != 2){
			throw new \Exception('你的账户还未通过审核');
		}

        /*
        if($record['status'] != 1) {
            //$loginLogModel->log($loginLog);

            throw new \Exception('此会员不允许登陆');
        }
         */

        /**
         * 此处拦截异常，主要用于主服务器无法访问时忽略异常
         */
        try {
            $expr = array('id' => $record['id']);
            $dao  = array(
                'last_login_time' => date('Y-m-d H:i:s'),
                'login_num'       => array('exp', 'login_num+1'),
                'last_login_ip'   => getClientIp(),
            );

            $this->where($expr)->save($dao);

            //$mLoginLog->log($loginLog);
        } catch(\Exception $e) {
        }

        return $record;
    }

    public function logout($appName = null) {
        $appName = empty($appName) ? MODULE_NAME : $appName;

        /**
         * 如果退出操作不是在登陆进入的平台执行的，那么就只清除指定平台的会员信息
         */
        if (MODULE_NAME != $_SESSION[self::LOGIN_APP_KEY]) {
            if(!empty($_SESSION[self::LOGIN_USER_KEY][$appName])) {
                unset($_SESSION[self::LOGIN_USER_KEY][$appName]);
            }

            return true;
        }

        $_SESSION = array();

        if(ini_get('session.use_cookies')) {
            $cookieOpt = session_get_cookie_params();

            setcookie(  session_name(),
                        '',
                        time() - 3600,
                        $cookieOpt['path'],
                        $cookieOpt['domain'],
                        $cookieOpt['secure'],
                        $cookieOpt['httponly']
            );
        }

        session_destroy();

        return true;
    }

    /**
     * getLoginToUrlByType
     * 根据会员类型返回登陆成功后跳转到的地址
     *
     * 此方法只针对站长和广告主才能返回正确的值
     *
     * 成功返回地址，失败返回false
     *
     * @param mixed $type
     * @return void
     */
    public function getLoginToUrlByType($type) {
        $type = (int)$type;
        $conf = C('LOGIN_TO_URL');

        return array_key_exists($type, $conf) ? $conf[$type] : false;
    }

    /**
     * checkExistsName
     * 检测用户名是否存在
     *
     * @param mixed $name
     * @param mixed $uid
     * @return void
     */
    public function checkExistsName($name, $uid = null) {
        $expr = array('name' => $name);
        $uid  = empty($uid) ? 0 : (int)$uid;

        if(!empty($uid)) {
            $expr['uid'] = array('NEQ', $uid);
        }

        return $this->where($expr)->find();
    }

    public function save2($d) {
        $error  = array();
        $action = 'save';

        if(empty($d['id'])) {
            try {
                $d['name'] = empty($d['name']) ? '' : strtolower(trim($d['name']));
                $this->checkUserName($d['name']);
            } catch(\Exception $e) {
                $error[] = $e->getMessage();
            }

            if(!empty($error)) {
                throw new \Exception(implode("\n", $error));
            }

            $action = 'add';
            $d['create_time'] = date('Y-m-d H:i:s');
            $d['create_ip']   = getClientIp();
        } 

        try {
            $d['passwd'] = empty($d['passwd']) ? null : $d['passwd'];

            if(!is_null($d['passwd'])) {
                $this->checkPassword($d['passwd']);
                $d['passwd'] = md5($d['passwd']);
            } else {
                unset($d['passwd']);
            }
        } catch(\Exception $e) {
            throw new \Exception($e->getMessage());
        }

        return $this->$action($d);
    }

    public function checkUserName($name) {
        $name = trim($name);
        $len  = strlen($name);

        if($len < C('USER_NAME_MIN_LEN') || $len > C('USER_NAME_MAX_LEN')) {
            throw new \Exception('登陆名称长度最少' . C('USER_NAME_MIN_LEN') .'个字符，最多' . C('USER_NAME_MAX_LEN') . '个字符');
        }

        if(!isSafeUserName($name)) {
            throw new \Exception('登陆名称必须由字母开头，可由字母、数字、横线、下划线组成');
        }

        if($this->checkExistsName($name)) {
            throw new \Exception('登陆名称已存在');
        }

        return true;
    }

    public function checkPassword($password) {
        $len = strlen($password);

        if($len < C('PASSWD_MIN_LEN') || $len > C('PASSWD_MAX_LEN')) {
            throw new \Exception('密码长度最少' . C('PASSWD_MIN_LEN') . '个字符，最多' . C('PASSWD_MAX_LEN') . '个字符');
        }

        return true;
    }

    /**
     * switchToById
     * 切换至会员平台
     *
     * @param mixed $id
     * @return void
     */
    public function switchToById($id) {
        $id = (int)$id;

        if (empty($id)) {
            throw new \Exception('请指定会员编号');
        }

        if (!$record = $this->where(['id' => $id])->find()) {
            throw new \Exception('没有找到此会员');
        }

        if (!$appName = $this->getAppNameByType($record['type'])) {
            throw new \Exception('会员类型有误');
        }

        if (!$toUrl = $this->getLoginToUrlByType($record['type'])) {
            throw new \Exception('会员类型有误');
        }

        $this->setLoginUser($record, $appName);
        setcookie('menu-index', 0); //重置menu-index
        header('location:' . $toUrl);
    }

    /**
     * getAppNameByType
     * 返回会员类型对应的项目名称
     *
     * @param mixed $type
     * @return void
     */
    public function getAppNameByType($type) {
        $conf = C('USER_TYPE_TO_MODULE_NAME');
        $type = (int)$type;

        return array_key_exists($type, $conf) ? $conf[$type] : false;
    }

    /**
     * buildSearchExpr 
     * 组装查询条件
     * 
     * @param mixed $d  查询数据，一般来源于$_GET或$_POST
     * @param mixed $field  可查询的字段对照表，其结构为array(array('表单项目名称' => '数据库字段名称'))
     * @access public
     * @return void 返回值为数组，参见方法内部注释
     */
    public function buildSearchExpr($d, $field = NULL) {
        $expr = array();
        $dao  = array();
        $sortExpr = $this->pk . ' DESC';

        /**
         * 下标为表单中的项目名称，值为数据库中表的字段名称 
         */
        if(empty($field)) {
            $field = array(
                'id'              => 'id',
                'name'            => 'name',
                'status'          => 'status',
                'email'           => 'email',
                'service_id'      => 'service_id',
                'referr'          => 'referr',
                'qq'              => 'qq',
                'add_day'         => 'reg_time',        //form中使用键reg_day，在url中reg会被转义成注册商标的标志字符
                'add_day_2'       => '',
                'last_login_day'  => 'last_login_time',
                'limit_ad_domain' => 'limit_ad_domain',
                'recommend'       => 'recommend',       //推荐人
                'money_type'      => '',                //余额类型
                'money_op'        => '',                //余额运算符
                'money_val'       => '',                //余额值
            );
        }

        foreach($field as $k => $v) {
            $tmpExpr = null;
            $val     = isset($d[$k]) ? trim($d[$k]) : '';

            if(strlen($val) < 1) {
                continue;
            }

            switch($k) {
                case 'name' :
                    $expr['name'] = array('EXP', "LIKE '%{$val}%'");
                    break;

                case 'email' : 
                    $expr['email'] = array('EXP', "LIKE '%{$val}%'");
                    break;

                case 'qq':
                    $expr['qq'] = array('EXP', "LIKE '%{$val}%'");
                    break;

                case 'referr' :
                    $expr['referr'] = array('EXP', "LIKE '%{$val}%'");
                    break;

                case 'add_day_2':
                    continue;
                    break;

                case 'add_day' : 
                    if(!strtotime($val)) {
                        continue; 
                        break;
                    }

                    if(empty($d['add_day_2'])) {
                        $tmpExpr = "TO_CHAR(reg_time, 'YYYY-MM-DD') = '{$val}'";
                    } else {
                        if(!strtotime($d['add_day_2'])) {
                            continue;
                            break;
                        }

                        $tmpExpr = "TO_CHAR(reg_time, 'YYYY-MM-DD') BETWEEN '{$val}' AND '{$d['add_day_2']}'";
                    }

                    break;

                case 'last_login_day' : 
                    $tmpExpr = "TO_CHAR(last_login_time, 'YYYY-MM-DD') = '{$val}'";
                    break;

                case 'money_val' :
                    $moneyType = empty($d['money_type']) ? null : strtolower(trim($d['money_type']));
                    $moneyOp   = empty($d['money_op'])   ? null : trim($d['money_op']);
                    $moneyVal  = (float)$d['money_val'];

                    $moneyOpConf = array(
                        1 => '>=',
                        2 => '<=',
                        3 => '=',
                    );
                    $moneyTypeConf = array(
                        'day'   => 'daymoney',
                        'week'  => 'weekmoney',
                        'month' => 'monthmoney',
                    );

                    $tmpMoneyOp = empty($moneyOpConf[$moneyOp]) ? null : $moneyOpConf[$moneyOp];

                    if($moneyType == 'total') {
                        $tmpField = '(daymoney+weekmoney+monthmoney)';
                    } else {
                        $tmpField = empty($moneyTypeConf[$moneyType]) ? null : $moneyTypeConf[$moneyType];
                    }

                    if(!empty($tmpMoneyOp) && !empty($tmpField) && !empty($moneyType) && !empty($moneyOp)) {
                        $tmpExpr = "{$tmpField}{$tmpMoneyOp}{$moneyVal}";
                    }

                    break;

                default:
                    $expr[$v] = $d[$k];
            }

            if(!empty($tmpExpr)) {
                $expr['_string'] = empty($expr['_string'])
                                  ? $tmpExpr
                                  : $expr['_string'] . " AND {$tmpExpr}";
            }

            $dao[$k] = $d[$k];
        }

        $sortField = array('id', 'name', 'money', 'money_1', 'money_2', 'money_3', 'status',
                            'last_login_time', 'login_num', 'service_id', 'limit_ad_domain', 'recommend');

        if(!empty($d['sort']) && in_array($d['sort'], $sortField)) {
            $sortdir = empty($d['sortdir'])                     ? 'DESC'   : strtoupper($d['sortdir']);
            $sortdir = in_array($sortdir, array('DESC', 'ASC')) ? $sortdir : 'DESC';

            if($d['sort'] == 'money') {
                $sort        = '(money_1 + money_2 + money_3)';
                $dao['sort'] = 'money';
            } else {
                $sort        = $d['sort'];
                $dao['sort'] = $sort;
            }

            $sortExpr       = "{$sort} {$sortdir}";
            $dao['sortdir'] = $sortdir;
        }

        $expr['type'] = static::TYPE;

        return array(
            'expr' => $expr,            //ThinkPHP查询条件表达式
            'sort' => $sortExpr,        //排序表达式
            'json' => json_encode($dao) //用于回填至查询表单的数据
        );
    }

    /**
     * _options_filter()中设置的有条件，所以在Model::save()方法中不会再设置以主键为条件
     */
    protected function _before_update(&$data, &$options) {
        if(!isset($data[$this->getPk()]) && empty($options['where'])) {
            return false;
        }

        if(isset($options['where']) && !array_key_exists($this->getPk(), $options['where'])) {
            $options['where'][$this->getPk()] = $data[$this->getPk()];
        }

        return true;
    }


    /**
     * changePasswd
     * 修改密码
     *
     * @param mixed $uid      会员编号
     * @param mixed $oldPasswd   旧密码
     * @param mixed $passwd  新密码
     * @return void
     */
    public function changePasswd($uid, $oldPasswd, $passwd) {
        $uid = (int)$uid;

        if(empty($uid)) {
            throw new \Exception('参数不完整');
        }

        if(!$rUser = $this->getByUid($uid)) {
            throw new \Exception('没有找到会员信息');
        }

        if($rUser['passwd'] != $this->getEncryptPasswd($oldPasswd)) {
            throw new \Exception('旧密码输入有误');
        }

        $newPasswdLen = strlen($passwd);
        $minLen       = C('PASSWD_MIN_LEN');
        $maxLen       = C('PASSWD_MAX_LEN');

        if($newPasswdLen < $minLen || $newPasswdLen > $maxLen) {
            throw new \Exception("密码最少{$minLen}个字符，最多{$maxLen}个字符");
        }

        $dao  = array('passwd' => $this->getEncryptPasswd($passwd));
        $expr = array('uid'      => $uid);

        return $this->where($expr)->save($dao);
    }

    /**
     * getEncryptPasswd
     * 获取加密后的密码
     *
     * @param mixed $passwd
     * @return void
     */
    public function getEncryptPasswd($passwd) {
        return md5($passwd);
    }

    /**
     * changeStatus
     * 修改会员状态
     *
     * todo:如果是锁定广告主，那么其广告计划也应该被停止
     *
     * @param mixed $id
     * @param mixed $s
     * @return void
     */
    public function changeStatus($id, $s) {
        $id = intvals($id);
        $s  = intvals($s);

        if (!array_key_exists($s, C('USER_STATUS'))) {
            throw new \Exception('会员状态有误');
        }

        return $this->where(['id' => $id])->save(['status' => $s]);
    }
	
	public function reg($post){
		$ader = array(
			array('name',"/[a-zA-Z0-9_]{4,16}/",'用户格式不正确',1,'regex'),
			array('name','','帐号名称已经存在！',2,'unique',1),
			array('passwd',"/^[A-Za-z0-9]{6,20}$/",'密码格式不正确',1,'regex'),
			array('repwd','passwd','两次输入密码不一致',1 ,'confirm'),
			array('question',"/^[\x{2E80}-\x{9FFF}]+$/u",'问题必须为汉字',1,'regex'),
			array('answer',"/^[\x{2E80}-\x{9FFF}]+$/u",'答案必须为汉字',1,'regex'),
			array('qq',"/^[1-9]\d{5,9}$/",'qq格式不对',1,'regex'),
			//array('weixin',"require",'微信不能为空',1,'regex'),
			array('email',"email",'邮箱格式不对',1,'regex'),
			array('tel',"/^1[358][0-9]{9}$/",'手机号码不正确',1,'regex'),
			array('protocol','require','请阅读微客广告协议',1),
		);
		$siter = array(
			array('name',"/[a-zA-Z0-9_]{4,16}/",'用户格式不正确',1,'regex'),
			array('name','','帐号名称已经存在！',2,'unique',1),
			array('passwd',"/^[A-Za-z0-9]{6,20}$/",'密码不正确',1,'regex'),
			array('repwd','passwd','两次输入密码不一致',1,'confirm'),
			array('question',"/^[\x{2E80}-\x{9FFF}]+$/u",'问题必须为汉字',1,'regex'),
			array('answer',"/^[\x{2E80}-\x{9FFF}]+$/u",'答案必须为汉字',1,'regex'),
			array('kf',"/^[1-2]{1}$/",'请选择客服',1,'regex'),
			array('qq',"/^[1-9]\d{5,9}$/",'qq格式不对',1,'regex'),
			//array('weixin',"require",'微信不能为空',1,'regex'),
			array('email',"email",'邮箱格式不对',1,'regex'),
			array('tel',"/^1[358][0-9]{9}$/",'手机号码不正确',1,'regex'),
			array('bank',"/^[a-z]{2,5}$/",'请选择银行',1,'regex'),
			array('accountname',"/^[\x{2E80}-\x{9FFF}]{2,4}$/u",'银行账户必须为2-4位的汉字',1,'regex'),
			array('bankacc',"/^\d{19}$/",'必须是19位正确的银行账卡号',1,'regex'),
			array('bankname',"require",'开户行地址不能为空',1),
			array('protocol','require','请阅读微客广告协议',1),
		);
		if(!array_key_exists('bank',$post)){
			//广告主2
			$post['type']=2;
			$data = $this->validate($ader)->create($post);
			
		}else{
			//站长1
			$post['type']=1;
			$data = $this->validate($siter)->create($post);
		}
		if(!$data){
			$error = $this->getError();
			throw new \Common\Exception\Exception($error);
		}
		$data['passwd']=$this->getEncryptPasswd($data['passwd']);
		$data['create_time']=date('Y-m-d H:i:s');
		$data[status]=0;
		switch ($data['contact']){
			case 1:
				$data['contact'] = '站长客服';
				break;
			case 2:
				$data['contact'] = '广告客服';
				break;
		}

		$this->add($data);
		
	}

}
